package com.example;

import com.example.annotation.type.Component;
import com.example.annotation.type.Controller;
import com.example.annotation.type.Service;
import com.example.utils.AnnotationUtils;
import com.example.utils.AssertUtils;
import com.example.utils.ClassUtils;

import java.io.File;
import java.net.URL;
import java.util.*;

public class BeanDefinitionReader {

    private Properties contextConfig = new Properties();

    private List<Class<?>> registryBeanClasses = new ArrayList<>();

    public List<BeanDefinition> loadBeanDefinitions(Class<?> originSourceClass) {
        ClassLoader classLoader = ClassUtils.getClassLoader(originSourceClass);
        doScanner(originSourceClass.getPackage().getName(), classLoader);
        List<BeanDefinition> result = new ArrayList<>();
        for (Class<?> registryBeanClass : registryBeanClasses) {
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setFactoryBeanName(toLowerFirstCase(registryBeanClass));
            beanDefinition.setBeanClassName(registryBeanClass.getSimpleName());
            beanDefinition.setBeanClass(registryBeanClass);
            beanDefinition.setPackageName(registryBeanClass.getPackage().getName());
            result.add(beanDefinition);
        }
        return result;
    }

    private void doScanner(String basePackageName, ClassLoader classLoader) {
        URL resource = classLoader.getResource(basePackageName.replace(".", "/"));
        if (resource == null) return;
        Set<String> pathSet = doScanFile(resource.getFile());
        String prefix = "classes";
        for (String path : pathSet) {
            String packageName = path.substring(path.indexOf(prefix) + prefix.length() + 1).replace("\\", ".");
            for (String className : Objects.requireNonNull(new File(path).list())) {
                if (className.endsWith("class")) {
                    String classPath = packageName + "." + className.substring(0, className.indexOf("."));
                    try {
                        Class<?> aClass = classLoader.loadClass(classPath);
                        if (aClass.isEnum() || aClass.isInterface() || aClass.isAnnotation() || !AnnotationUtils.isInclude(aClass)) {
                            continue;
                        }
                        registryBeanClasses.add(aClass);
                    } catch (ClassNotFoundException e) {
                        throw new IllegalArgumentException("class not found " + classPath);
                    }
                }
            }
        }
    }

    private Set<String> doScanFile(String path) {
        Set<String> fileSet = new HashSet<>();
        File file = new File(path);
        for (File listFile : Objects.requireNonNull(file.listFiles())) {
            if (listFile.isFile()) {
                fileSet.add(file.getPath());
            } else if (listFile.isDirectory()) {
                fileSet.addAll(doScanFile(listFile.getPath()));
            }
        }
        return fileSet;
    }


    /**
     * 首字母小写
     */
    private String toLowerFirstCase(Class<?> classZ) {
        String name = "";
        if (classZ.isAnnotationPresent(Service.class)) {
            name = classZ.getAnnotation(Service.class).value();
        } else if (classZ.isAnnotationPresent(Controller.class)) {
            name = classZ.getAnnotation(Controller.class).value();
        } else if (classZ.isAnnotationPresent(Component.class)) {
            name = classZ.getAnnotation(Component.class).value();
        }
        if (!name.equals("")) return name;
        Class<?> superclass = classZ.getSuperclass();
        Class<?>[] interfaces = classZ.getInterfaces();
        if (superclass == Object.class && interfaces.length == 0) {
            char[] chars = classZ.getSimpleName().toCharArray();
            return Character.toLowerCase(chars[0]) + String.copyValueOf(chars, 1, chars.length - 1);
        } else if (interfaces.length == 1) {
            char[] chars = interfaces[0].getName().toCharArray();
            return Character.toLowerCase(chars[0]) + String.copyValueOf(chars, 1, chars.length - 1);
        }
        throw new IllegalArgumentException(String.format("class %s unknown to name it", classZ.getSimpleName()));
    }

}
